package com.clothes.aweiDoExercises.sentinel;

import cn.hutool.core.io.FileUtil;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Component
public class SentinelRulesManager {

    @PostConstruct
    public void initRules() throws Exception {
        initFlowRules();
        initDegradeRules();
        listenRules ();
    }


    // 限流规则配置：针对方法参数进行热点限流
    public void initFlowRules() {
        ParamFlowRule rule = new ParamFlowRule("listQuestionVOByPage")
                .setParamIdx(0)             // 对方法的第1个参数进行限流（参数索引从0开始）
                .setCount(60)              // 每秒允许通过的请求数上限为30
                .setDurationInSec(60);     // 统计窗口时间为60秒
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }

    // 降级规则配置：包含慢调用比例和错误率两种降级策略
    public void initDegradeRules() {
        // 慢调用比例降级规则：当请求响应时间过长时触发熔断
        DegradeRule slowCallRule = new DegradeRule("listQuestionVOByPage")
                .setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()) // 慢调用比例策略
                .setCount(0.2)                     // 慢调用比例阈值：响应时间超过阈值的请求占比≥20%时触发熔断
                .setTimeWindow(20)                 // 熔断时间窗口：熔断后保持60秒
                .setStatIntervalMs(30 * 1000)      // 统计时长：采用30秒的窗口进行统计
                .setMinRequestAmount(10)           // 最小请求数：至少需要10次请求才进行统计
                .setSlowRatioThreshold(3);         // 慢调用阈值：响应时间超过3秒的请求视为慢调用

        // 错误率降级规则：当请求错误率过高时触发熔断
        DegradeRule errorRateRule = new DegradeRule("listQuestionVOByPage")
                .setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())  // 错误率策略
                .setCount(0.1)                     // 错误率阈值：异常请求占比≥10%时触发熔断
                .setTimeWindow(20)                 // 熔断时间窗口：熔断后保持60秒
                .setStatIntervalMs(30 * 1000)      // 统计时长：采用30秒的窗口进行统计
                .setMinRequestAmount(10);          // 最小请求数：至少需要10次请求才进行统计

        // 加载两种降级规则
        DegradeRuleManager.loadRules(Arrays.asList(slowCallRule, errorRateRule));
    }


    /**
     * 动态监听 Sentinel 规则文件变化并自动刷新规则
     * 支持从文件加载规则，并将规则变更写回文件
     */
    public void listenRules() throws Exception {
        // 获取项目根目录
        String rootPath = System.getProperty("user.dir");
        // 创建 sentinel 规则文件夹（如果不存在）
        File sentinelDir = new File(rootPath, "sentinel");
        if (!FileUtil.exist(sentinelDir)) {
            FileUtil.mkdir(sentinelDir);
        }

        // 定义限流规则和降级规则的文件路径
        String flowRulePath = new File(sentinelDir, "FlowRule.json").getAbsolutePath();
        String degradeRulePath = new File(sentinelDir, "DegradeRule.json").getAbsolutePath();

        // ===== 限流规则配置 =====
        // 创建文件数据源，自动监听文件变化并解析为 FlowRule 列表
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource =
                new FileRefreshableDataSource<>(flowRulePath, flowRuleListParser);

        // 将数据源注册到 Sentinel，使规则变更自动生效
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());

        // 创建可写数据源，用于将规则变更写回文件
        WritableDataSource<List<FlowRule>> flowWds =
                new FileWritableDataSource<>(flowRulePath, this::encodeJson);

        // 注册可写数据源，支持通过 Sentinel Dashboard 修改规则并持久化
        WritableDataSourceRegistry.registerFlowDataSource(flowWds);

        // ===== 降级规则配置 =====
        // 同样的方式配置降级规则的文件监听和持久化
        FileRefreshableDataSource<List<DegradeRule>> degradeRuleDataSource =
                new FileRefreshableDataSource<>(degradeRulePath, degradeRuleListParser);

        DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());

        WritableDataSource<List<DegradeRule>> degradeWds =
                new FileWritableDataSource<>(degradeRulePath, this::encodeJson);

        WritableDataSourceRegistry.registerDegradeDataSource(degradeWds);
    }

    // JSON 解析器：将文件内容转换为 FlowRule 列表
    private Converter<String, List<FlowRule>> flowRuleListParser = source ->
            JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});

    // JSON 解析器：将文件内容转换为 DegradeRule 列表
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source ->
            JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {});

    // JSON 编码器：将规则对象转换为 JSON 字符串
    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}
